package org.hackystat.devcathlon.engine.event.impl;

import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.dailyprojectdata.client.DailyProjectDataClient;
import org.hackystat.dailyprojectdata.resource.build.jaxb.BuildDailyProjectData;
import org.hackystat.dailyprojectdata.resource.build.jaxb.MemberData;
import org.hackystat.dailyprojectdata.resource.commit.jaxb.CommitDailyProjectData;
import org.hackystat.devcathlon.engine.event.Event;
import org.hackystat.devcathlon.engine.event.EventException;
import org.hackystat.devcathlon.engine.event.EventScore;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.match.MatchException;
import org.hackystat.devcathlon.engine.team.Team;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.utilities.logger.HackystatLogger;
import org.hackystat.utilities.tstamp.Tstamp;

/**
 * Awards points for a passing CI build and deducts points for a failed CI build.
 * <p>
 * Requires the following properties in the configuration.xml file for this event:
 * <ul>
 * <li>WakeupInterval: How frequently this event is invoked for looking for a successful CI build.
 * <li>SuccessWeight: The integer given for one successful CI build.
 * <li>ChurnThreshold: An integer indicating the threshold for churn, such as 50 lines of code.
 * </ul>
 * <p>
 * Specification:
 * <ul>
 * <li>Checks for "continuous.integration" build data since the last wakeup interval for all daily
 * project build data.
 * <li>Retrieves build daily project data if there is a successful build.
 * <li>Retrieves commit dailly project data if the churn is greater than the churn threshold.
 * <li>Base Score: 0 if no previous build data exists. Otherwise base score is +5 depending upon a
 * successful CI builds and amount of change in code since wakeup of this event.
 * </ul>
 * 
 * @author Ka Hung Phillip Lau & Robin Ranqueno
 * 
 */
public class KeepTheRepositoryCleanDaily extends Event {

  /** Support serialization. */
  private static final long serialVersionUID = 1L;
  /** Name of the event. */
  public static final String eventName = "Keep The Repository Clean Daily";
  /** Description of the event. */
  public static final String description = "Penalize (or award) developers for committing code "
      + "that fails (or passes) a continuous integration build. ";
  /** The actual success build point weight. */
  private int successWeight;
  /** The churn threshold key. */
  public static final String churnThresholdKey = "ChurnThreshold";
  /** The actual churn threshold. */
  private int churnThreshold;
  /** The property key for successWeight. */
  public static final String successWeightKey = "SuccessWeight";

  /**
   * Creates a new event for Keep The Repository Clean Daily.
   * 
   * @throws EventException If the configuration cannot be created.
   */
  public KeepTheRepositoryCleanDaily() throws EventException {
    super(eventName, description);
    try {
      successWeight = Integer.parseInt(configManager.getProperty(eventName, successWeightKey));
      churnThreshold = Integer.parseInt(configManager.getProperty(eventName, churnThresholdKey));
    }
    catch (Exception e) {
      throw new EventException("Error configuring properties", e);
    }
  }

  /**
   * Returns the total number of points for this event.
   * 
   * @param score The event score.
   * @return The total points for this event.
   */
  public int computePoints(EventScore score) {
    return score.getBaseScore();
  }

  /**
   * Creates an EventScore instance for EveryoneBuilds.
   * 
   * @param match The match of interest.
   * @param team The team of interest.
   * @param timestamp The time at which this event is being run.
   * @return The EventScore.
   * @throws EventException If problems occur while processing this event.
   * @throws MatchException If an error occurs.
   */
  public EventScore computeScore(Match match, Team team, XMLGregorianCalendar timestamp)
      throws EventException, MatchException {

    EventScore score = new EventScore(timestamp, match, team);
    Project project = match.getProject(team);
    String projectName = project.getName();
    String owner = project.getOwner();
    DailyProjectDataClient dpdClient = match.getDailyProjectDataClient(team);
    XMLGregorianCalendar startTime = Tstamp.incrementMinutes(timestamp, wakeupInterval * -1);

    HackystatLogger.getLogger("org.hackystat.devcathlon", "devcathlon").info(
        String.format("Getting build Data for match %s, team %s", match.getName(), team.getName()));

    int buildSuccess = 0;
    int linesAdded = 0;
    int linesDeleted = 0;
    int commitChurn = 0;

    try {
      // Get build data.
      BuildDailyProjectData buildData = dpdClient.getBuild(owner, projectName, startTime,
          "continuous.integration");

      for (MemberData memBuildData : buildData.getMemberData()) {
        buildSuccess = memBuildData.getSuccess();
      }

      // Get commit data, if theres a successful build.
      if (buildSuccess > 0) {
        CommitDailyProjectData commitData = dpdClient.getCommit(owner, projectName, startTime);

        for (org.hackystat.dailyprojectdata.resource.commit.jaxb.MemberData memComData : commitData
            .getMemberData()) {
          linesAdded = memComData.getLinesAdded();
          linesDeleted = memComData.getLinesDeleted();
        }
      }
    }
    catch (Exception e) {
      throw new EventException(String.format("Error getting Build data for match %s, team %s",
          match.getName(), team.getName()), e);
    }

    // Check if churn is more than threshold.
    commitChurn = (linesAdded + linesDeleted);
    if (commitChurn >= churnThreshold) {
      score.setBaseScore(successWeight);
    }

    else {
      HackystatLogger.getLogger("org.hackystat.devcathlon", "devcathlon").info(
          String.format("No change in churn."));
    }
    return score;
  }
}
